作者:陈广 日期:2019-2-18
通过本实验,掌握 Mifare S50 卡的主动选卡以及被动选卡操作,单张卡片选卡以及多卡片选卡操作。
Mifare S50 是 ISO14443A 协议卡,此类型卡安全性较高,多用于公交车卡,银行卡,社保卡、消费卡。一般情况下,ISO14443 卡都是单张读取并操作。
在选择一个新的电子标签进行操作前,必须首先执行请求(Request)命令,为应对读写器上放有多张标签的情况,接下来要执行防冲突(Anticoll)命令,防冲突命令会返回多张标签中的一张的 UID 号,此时再进行选择(Select)此标签操作。
接下来我们编程进行选择一张卡片的操作。新建一个控制台应用程序,输入如下代码:
using System;
using System.Threading.Tasks;
using HBLib;
using HBLib.HR8002Reader;
using HBLib.ISO14443A;
namespace CmdDemo
{
class Program
{
static ComPort com = new ComPort("COM3", 19200, 1000);
static void Main(string[] args)
{
com.Open();
Select();
Console.ReadLine();
}
private static async Task Select()
{
Reader reader = new Reader(0x00, com);
I14443A i14443a = new I14443A(0x00, com);
//请求操作
await reader.ChangeToISO14443AAsync();
var info = await i14443a.RequestAsync(RequestMode.AllCard);
if (info.ReturnValue != ReturnMessage.Success)
{
Console.WriteLine(info.GetStatusStr());
return;
}
//防冲突操作
var info1 = await i14443a.AnticollAsync();
if (info1.ReturnValue != ReturnMessage.Success)
{
Console.WriteLine(info1.GetStatusStr());
return;
}
//选卡操作
var info2 = await i14443a.SelectAsync(info1.UID);
if(info2.ReturnValue==ReturnMessage.Success)
{
Console.WriteLine("选卡成功,选中卡片:" + info1.GetUIDStr() + "。卡片容量标志为:" + info2.Size.ToString());
}
else
{
Console.WriteLine(info1.GetStatusStr());
}
}
}
}
运行程序,在读写器上不放卡片、放一张卡片、放多张卡片,运行程序,查看各自的效果。
这个系列文章,包括将来出的书中,会出现较多的思考题,你也可以理解为作业。我之前出的书,很多人会问我要习题答案,我当然会给。教书这么多年,我现在有了新的想法,至少在程序设计这一块,不应该有标准答案。我们更应注重培养的应当是解决问题的能力,将来工作时,遇到问题,不大可能会有人给你标准答案,很多的东西需要你自己摸索、解决。我自己就是这么过来的,在解决问题的过程中水平不断提高 。解决问题的能力是在不断痛苦折磨中锤炼出来的,给标准答案不会对这种重要的能力有任何帮助。我提出要求,只要你能按照要求完成,就是 100 分。至于你是怎么完成的,完成的质量如何,代码漂不漂亮,这些并不是最重要的,首先你要迈出这一步。最后一句话总结,将来我出的所有思考题都不会有任何答案。
下面我出一道思考题,让大家尝试一下:
思考题(难度 *):编写程序,每隔 5 秒钟进行一次选卡操作,如果选卡成功则蜂鸣器响一声。编写完成后在读写器上放 6 张卡片,看看是否能读取所有卡片。
什么是被动选卡呢?坐地铁刷卡就是被动选卡,当我们将地铁卡放到感应场,嘀一声,闸门打开,我们通过。当读写器感应到有卡片进入感应场,主动向上位机发出信号,这就是被动选卡。我们在上一个实验做的就是主动选卡:将卡片放到读写器,启动程序或点击上位机程序按钮,命令读写器读卡。HR8002 的命令只能由上位机发起,所以无法实现真正意义上的被动选卡。若要实现被动选卡功能,只能由上位机不停地向读写器发送选卡命令,当读写器返回选卡成功信息,则说明有标签进入感应场。
将程序更改如下:
static ComPort com = new ComPort("COM3", 19200, 1000);
static void Main(string[] args)
{
com.Open();
Select();
Console.ReadLine();
}
private static async Task Select()
{
Reader reader = new Reader(0x00, com);
I14443A i14443a = new I14443A(0x00, com);
string uidStr = "";
await reader.ChangeToISO14443AAsync();
while (true)
{
//请求操作
var info = await i14443a.RequestAsync(RequestMode.AllCard);
if (info.ReturnValue == ReturnMessage.Success)
{
//防冲突操作
var info1 = await i14443a.AnticollAsync();
if (info1.ReturnValue == ReturnMessage.Success)
{
//选卡操作
var info2 = await i14443a.SelectAsync(info1.UID);
if (info2.ReturnValue == ReturnMessage.Success)
{
string uid = info1.GetUIDStr();
if (uidStr != uid)
{ //有新卡片进入感应场
uidStr = uid;
Console.WriteLine("选卡成功,选中卡片:" + uidStr + "。卡片容量标志为:" + info2.Size.ToString());
await reader.BeepAsync(5, 0, 1); //响一声
}
}
}
}
else
{
if (uidStr != "") //感应场内无卡
{
uidStr = "";
Console.WriteLine("无卡");
}
}
Thread.Sleep(300);
}
}
运行程序,轮流放几张卡片进入然后离开感应场,程序运行结果如下。
选卡成功,选中卡片:8774DB2A。卡片容量标志为:8
无卡
选卡成功,选中卡片:47E0F62A。卡片容量标志为:8
无卡
选卡成功,选中卡片:8774DB2A。卡片容量标志为:8
无卡
选卡成功,选中卡片:8774DB2A。卡片容量标志为:8
无卡
选卡成功,选中卡片:47E0F62A。卡片容量标志为:8
无卡
当卡片进入感应场,选中此张卡片;当卡片离开感应场,显示无卡。基本实现我们平日所见的被动读卡功能。
思考题(难度 *):但此程序只是针对单张卡片进入感应场的情况,如果多张卡片进入感应场,则会不停地交替选中多张卡片。请更改程序,当多张卡片进入感应场时,只会选中其中一张卡片,且不会重复选择。
虽然 ISO14443A 卡一般情况下不会有同时读取多卡的需求。但做为学习者我们依然可以尝试实现这个功能。如果我们使用上一个程序来同时读取多张卡片,会发现大部分情况下只会读出 2 张卡片信息,有时会读出 3 张卡片信息。如果我在读写器上放了 6 张,甚至 10 张卡片,要求全部列出它们的卡号,该如何实现呢?我们可以每感应到一张卡片就将其置于休眠状态,以便可以读取其它卡片,直至所有卡片进入休眠状态。
为帮助大家理解这个过程,我做了一个 Demo(源码会随同 HBLib 库一起放在 GitHub),让大家以手动的方式感应多张卡片。
打开 Demo 程序,选择菜单项【RFID】➤【ISO14443A】➤【感应多张卡片】打开此程序,如下图所示:
前面 4 个命令我们之前都已经讲过,此程序多加了一个休眠命令。我们在读卡中放置 6 张以上卡片,此时我们以两种形式进行实验:
我自己操作的结果是,如果在不移动卡片的情况下,很难将所有卡片选中。有时卡片休眠了,还可以被防冲突选择。总之在超过 2 张卡片的情况下读卡器不太稳定。同时也说明了 13.56MHz 并不适用于同时选中多张卡片的操作。
;思考题(难度 ***):编写程序,显示感应场内所有标签的 UID,假设感应场内有 6 张标签,当感应完所有 6 标签后程序停止。完成时间越快,分数越高。